package com.grandtech.insurance.service.impl;

import com.grandtech.insurance.cache.CacheTileScheme;
import com.grandtech.insurance.common.bean.SqlVo;
import com.grandtech.insurance.common.compress.GZip;
import com.grandtech.insurance.common.core.bean.Tile;
import com.grandtech.insurance.common.core.bean.TileSchemeCore;
import com.grandtech.insurance.common.core.coding.VectorTileEncoder;
import com.grandtech.insurance.common.core.dao.ITileDao;
import com.grandtech.insurance.common.core.utils.MercatorProjection;
import com.grandtech.insurance.common.core.utils.TileUtils;
import com.grandtech.insurance.common.db.mongdb.TileTaskSync;
import com.grandtech.insurance.common.db.mongdb.dao.IMongoDao;
import com.grandtech.insurance.common.utils.trans.MapUtil;
import com.grandtech.insurance.common.utils.trans.TileBound;
import com.grandtech.insurance.common.utils.trans.TileBoundReq;
import com.grandtech.insurance.common.utils.trans.Transformation;
import com.grandtech.insurance.dao.app.MongoDao;
import com.grandtech.insurance.service.app.ITileService;
import com.mapbox.geojson.BoundingBox;
import com.mapbox.geojson.Feature;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.WKBReader;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 切瓦片
 */
@Service
public class TileServiceImpl implements ITileService {

    Log log = LogFactory.getLog(TileServiceImpl.class);

    @Autowired
    ITileDao iTileDao;

    @Autowired
    IMongoDao<Tile> iMongoDao;


    @Override
    public void tileByFeature(String tableName, Feature feature, Map cacheRecord) {
        try {
            TileSchemeCore tileSchemeCore = CacheTileScheme.getInstance().getSchemeByName(tableName);
            iMongoDao.setMongoTemplate(tileSchemeCore.getMongoTemplate());
            BoundingBox boundingBox = Transformation.getBoxRatio(feature, 1.1);
            tileByBound(tableName, boundingBox, cacheRecord);
            boolean isTileSymbol = tileSchemeCore.getTileSymbol();
            String symbolTileId = tileSchemeCore.getSymbolTileId();
            if (isTileSymbol) tileByBound(symbolTileId, boundingBox, cacheRecord);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void tileByBound(String tableName, BoundingBox boundingBox, Map cacheRecord) throws Exception {
        double west = boundingBox.west();
        double south = boundingBox.south();
        double east = boundingBox.east();
        double north = boundingBox.north();
        tileByRegion(tableName, west, south, east, north, cacheRecord);
    }

    @Override
    public void tileByRegion(String tableName, double west, double south, double east, double north, Map cacheRecord) throws Exception {
        TileSchemeCore tileSchemeCore = CacheTileScheme.getInstance().getSchemeByName(tableName);
        if (tileSchemeCore.getBeforeTile() != null) iTileDao.exeSql(new SqlVo(tileSchemeCore.getBeforeTile()));

        iMongoDao.setMongoTemplate(tileSchemeCore.getMongoTemplate());
        int zMin = tileSchemeCore.getMinZoom();
        int zMax = tileSchemeCore.getMaxZoom();
        String mongoTileName = tileSchemeCore.getMongoTileName();
        Tile tile;
        Query query;
        StringBuilder message;
        StringBuilder cacheId;
        for (int z = zMin; z <= zMax; z++) {
            long sx = MercatorProjection.longitudeToTileX(west, (byte) z);
            long ey = MercatorProjection.latitudeToTileY(south, (byte) z);
            long ex = MercatorProjection.longitudeToTileX(east, (byte) z);
            long sy = MercatorProjection.latitudeToTileY(north, (byte) z);
            for (long x = sx; x <= ex; x++) {
                for (long y = sy; y <= ey; y++) {

                    cacheId = new StringBuilder();
                    cacheId.append(tableName).append(z).append("-").append(x).append("-").append(y);
                    if (cacheRecord.containsKey(cacheId.toString())) continue;
                    cacheRecord.put(cacheId.toString(), tableName);

                    message = new StringBuilder();
                    message.append(tableName).append(" 切片[").append(z).append("|").append(x).append("|").append(y).append("]");
                    System.out.println(message);
                    tile = getBytes(tileSchemeCore, z, x, y);
                    query = new Query();
                    query.addCriteria(Criteria.where("zoom_level").is(z).and("tile_column").is(x).and("tile_row").is(y));
                    iMongoDao.saveOrUpdate(tile, query, mongoTileName);
                }
            }
        }
    }

    @Override
    public void tileByFeature(String tableName, Feature feature) {
        try {
            TileSchemeCore tileSchemeCore = CacheTileScheme.getInstance().getSchemeByName(tableName);
            iMongoDao.setMongoTemplate(tileSchemeCore.getMongoTemplate());
            BoundingBox boundingBox = Transformation.getBoxRatio(feature, 1.1);
            tileByBound(tableName, boundingBox);
            boolean isTileSymbol = tileSchemeCore.getTileSymbol();
            String symbolTileId = tileSchemeCore.getSymbolTileId();
            if (isTileSymbol) tileByBound(symbolTileId, boundingBox);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void tileByBound(String tableName, BoundingBox boundingBox) throws Exception {
        double west = boundingBox.west();
        double south = boundingBox.south();
        double east = boundingBox.east();
        double north = boundingBox.north();
        tileByRegion(tableName, west, south, east, north);
    }

    @Override
    public void tileByRegion(String tableName, double west, double south, double east, double north) throws Exception {
        TileSchemeCore tileSchemeCore = CacheTileScheme.getInstance().getSchemeByName(tableName);
        iMongoDao.setMongoTemplate(tileSchemeCore.getMongoTemplate());
        int zMin = tileSchemeCore.getMinZoom();
        int zMax = tileSchemeCore.getMaxZoom();
        String mongoTileName = tileSchemeCore.getMongoTileName();
        Tile tile;
        Query query;
        StringBuilder message;
        for (int z = zMin; z <= zMax; z++) {
            long sx = MercatorProjection.longitudeToTileX(west, (byte) z);
            long ey = MercatorProjection.latitudeToTileY(south, (byte) z);
            long ex = MercatorProjection.longitudeToTileX(east, (byte) z);
            long sy = MercatorProjection.latitudeToTileY(north, (byte) z);
            for (long x = sx; x <= ex; x++) {
                for (long y = sy; y <= ey; y++) {
                    message = new StringBuilder();
                    message.append(tableName).append(" 切片[").append(z).append("|").append(x).append("|").append(y).append("]");
                    System.out.println(message);
                    tile = getBytes(tileSchemeCore, z, x, y);
                    query = new Query();
                    query.addCriteria(Criteria.where("zoom_level").is(z).and("tile_column").is(x).and("tile_row").is(y));
                    iMongoDao.saveOrUpdate(tile, query, mongoTileName);
                }
            }
        }
    }

    @Override
    public Tile getBytes(TileSchemeCore tileSchemeCore, long z, long x, long y) throws Exception {
        iMongoDao.setMongoTemplate(tileSchemeCore.getMongoTemplate());
        String layerName = tileSchemeCore.getTileId();
        String sql = tileSchemeCore.getSqlTemplate();
        String shapeField = tileSchemeCore.getShapeField();
        String sqlTile = TileUtils.parseXyz2Bound(x, y, z);
        sql = sql.replace("?", sqlTile);
        List<Map> mapList = iTileDao.queryFeatures(new SqlVo(sql));
        VectorTileEncoder vte = new VectorTileEncoder(tileSchemeCore.getExtent(), tileSchemeCore.getClipBuffer(), tileSchemeCore.getAutoScale());
        Geometry shape;
        Map<String, Object> attribute;
        WKBReader wkbReader = new WKBReader();
        for (int i = 0; i < mapList.size(); i++) {
            attribute = mapList.get(i);
            if(StringUtils.isNotEmpty(attribute.get("geom").toString())){
                shape = wkbReader.read((byte[]) attribute.get("geom"));
                TileUtils.convert2Pixel(x, y, z, shape);
                attribute.remove(shapeField);
                attribute = MapUtil.sortMapKey(attribute);
                vte.addFeature("dt_ytdk0305", attribute, shape);
            }
        }
        byte[] data = vte.encode();
        data = GZip.compress(data);
        Tile tile = new Tile(z, x, y, 0, data);
        return tile;
    }

    @Autowired
    MongoDao mongoDao;
    @Override
    public void tileByBound(TileBoundReq req) throws Exception {
        try {
            TileSchemeCore tileSchemeCore = CacheTileScheme.getInstance().getSchemeByName(req.getTableName());
            mongoDao.setMongoTemplate(tileSchemeCore.getMongoTemplate());
            for (TileBound bound : req.getBounds()) {
                BoundingBox boundingBox = BoundingBox.fromLngLats(bound.getWest(), bound.getSouth(), bound.getEast(), bound.getNorth());
                tileByBound(req.getTableName(), boundingBox);
                boolean isTileSymbol = tileSchemeCore.getTileSymbol();
                String symbolTileId = tileSchemeCore.getSymbolTileId();
                if (isTileSymbol) tileByBound(symbolTileId, boundingBox);
            }
        } catch (Exception e) {
            throw e;
        }
    }

    @Override
    public void tileByFeature1(String tableName, Feature feature) {
        long start = System.currentTimeMillis();
        BoundingBox box = Transformation.getBoxRatio(feature, 1.1);
        List<BoundingBox> boxes = new ArrayList<>();
        boxes.add(box);
        TileSchemeCore tileSchemeCore = CacheTileScheme.getInstance().getSchemeByName(tableName);
        new TileTaskSync(tileSchemeCore, boxes).startTile();
        boolean isTileSymbol = tileSchemeCore.getTileSymbol();
        if (isTileSymbol) {
            String symbolTileId = tileSchemeCore.getSymbolTileId();
            TileSchemeCore symbolSchemeCore = CacheTileScheme.getInstance().getSchemeByTileId(symbolTileId);
            //切标注
            new TileTaskSync(symbolSchemeCore, boxes).startTile();
        }
        log.info("切片耗时[" + (System.currentTimeMillis() - start) + "]");
    }
}













